package com.zhaojun.cloud.support.shiro;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.zhaojun.cloud.common.pojo.User;
import com.zhaojun.cloud.common.util.JsonMapper;
import com.zhaojun.cloud.service.api.RedisService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;

/**
 * wwj
 * 2018/12/7  14:35
 * 自定义RedisCache操作器
 */
@Component
public class RedisCache implements Cache {

    @Value("${sso.redis.key}")
    private String redis_session_key;

    @Autowired
    private RedisService redisService;

    @Autowired
    private MyShiroRealm myShiroRealm;

    private static ObjectMapper objectMapper;

    /**
     * 这两个属性 不去掉 反序列化会出现问题
     */
    private final String attribute_valid = "valid";

    private final String attribute_attributeKeys = "attributeKeys";

    private final String attribute_attributes = "attributes";

    private final String attribute_primaryPrincipal = "primaryPrincipal";

    static {
        objectMapper = new ObjectMapper();
        objectMapper.registerModule(new SimpleModule().addDeserializer(Serializable.class, new StringDeserializer()));
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    private final Logger logger = LoggerFactory.getLogger(RedisCache.class);

    /**
     * 反序列化 有报错Serializable.class 需要定义一些配置
     */
    @Override
    public Object get(Object key) throws CacheException {
        try {
            logger.info("获取session:"+key.toString());
            String session = redisService.hget(redis_session_key, key.toString());
            if (StringUtils.isNotBlank(session)) {
                ObjectNode objectNode = (ObjectNode) objectMapper.readTree(session);
                objectNode.remove(attribute_valid);
                objectNode.remove(attribute_attributeKeys);
                JsonNode jsonNode = objectNode.get(attribute_attributes);

                SimpleSession simpleSession = objectMapper.readValue(objectNode.toString(), SimpleSession.class);
                Optional primaryPrincipal = Optional.ofNullable(jsonNode).map(j -> j.get(DefaultSubjectContext.PRINCIPALS_SESSION_KEY)).map(p -> p.get(attribute_primaryPrincipal));
                if (primaryPrincipal.isPresent()) {
                    User user = JsonMapper.INSTANCE.fromJson(primaryPrincipal.get().toString(), User.class);
                    simpleSession.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY,new SimplePrincipalCollection(user,myShiroRealm.getName()));
                }
                return simpleSession;
            }
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            throw new CacheException(e);
        }
    }

    @Override
    public Object put(Object key, Object value) throws CacheException {
        redisService.hset(redis_session_key, key.toString(), JsonMapper.INSTANCE.toJson(value));
        return null;
    }

    @Override
    public Object remove(Object key) throws CacheException {
        redisService.hdel(redis_session_key, key.toString());
        return null;
    }

    @Override
    public void clear() throws CacheException {
        redisService.del(redis_session_key);
    }

    @Override
    public int size() {
        return redisService.hsize(redis_session_key);
    }

    @Override
    public Set keys() {
        return redisService.hkeys(redis_session_key);
    }

    @Override
    public Collection values() {
        Collection<String> collections = redisService.hvalues(redis_session_key);
        Collection<Session> result = new ArrayList();
        try {
            for(String session: collections){
                ObjectNode objectNode = (ObjectNode) objectMapper.readTree(session);
                objectNode.remove(attribute_valid);
                objectNode.remove(attribute_attributeKeys);
                JsonNode jsonNode = objectNode.get(attribute_attributes);
                SimpleSession simpleSession = objectMapper.readValue(objectNode.toString(), SimpleSession.class);
                Optional primaryPrincipal = Optional.ofNullable(jsonNode).map(j -> j.get(DefaultSubjectContext.PRINCIPALS_SESSION_KEY)).map(p -> p.get(attribute_primaryPrincipal));
                if (primaryPrincipal.isPresent()) {
                    User user = JsonMapper.INSTANCE.fromJson(primaryPrincipal.get().toString(), User.class);
                    simpleSession.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, new SimplePrincipalCollection(user, myShiroRealm.getName()));
                }
                result.add(simpleSession);
            }
            return result;
        } catch (Exception e) {
            throw new CacheException(e);
        }
    }
}
